/* eslint-disable no-unused-vars */
/* eslint-disable getter-return */
// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import x264_encoder from '../3rd/video-codec-lib/X264Encoder'
import Util from '../Util'

class VideoEncode {
  constructor() {
    this.util = new Util()
    this.module = null
    this.h264_error = this.openEncoderError
    this._state = 'unconfigured'
    this.videoH264DataList = []
  }

  get encodeQueueSize() {
    console.log('unconfigured')
  }

  get state() {
    return this._state
  }

  getWasm(options) {
    this.util
      .loadWasm(`${options.x264WasmPath}/X264Encoder.wasm`)
      .then((wasmBinary) =>
        x264_encoder({
          instantiateWasm: (imports, successCallback) => {
            imports.env.h264_write_polyfill = (ptr, number) => {
              this.getH264Data(
                {
                  copyTo: (dest) => {
                    const len = dest.length
                    for (let i = 0; i < len; i++) {
                      dest[i] = this.module.HEAPU8[ptr + i]
                    }
                  },
                  byteLength: number
                },
                {}
              )
            }
            WebAssembly.instantiate(new Uint8Array(wasmBinary), imports).then((output) => {
              successCallback(output.instance)
            })
          }
        })
      )
      .then((module) => {
        this.module = module
        this.encoder = new this.module.X264Encoder()
        this.encoder.configure(options.width, options.height)
        this._state = 'configured'

        self.postMessage(
          {
            type: 'startEncode'
          },
          []
        )
      })
  }

  encode(frame) {
    if (this._state === 'unconfigured') {
      throw `Failed to execute 'encode' on 'VideoEncoder': Cannot call 'encode' on an unconfigured codec.`
    }
    if (!frame) {
      throw ''
    }

    const yuv = this.util.convertImageDataToYUV420(frame)
    const yuvPtr = this.module._malloc(yuv.byteLength)
    this.module.HEAPU8.set(yuv, yuvPtr)
    this.encoder.encode(yuvPtr)
    this.module._free(yuvPtr)
  }

  openEncoderError(e) {
    console.log(e.message)
  }

  getH264Data(chunk, opts, format = 'annexb') {
    let chunkData = new Uint8Array(chunk.byteLength)
    chunk.copyTo(chunkData)
    this.videoH264DataList.push(chunkData)

    if (this.videoH264DataList.length) {
      const itemH264Data = this.videoH264DataList.shift()
      self.postMessage(
        {
          type: 'encodeResult',
          data: itemH264Data
        },
        [itemH264Data.buffer]
      )
    }
  }
}

export default VideoEncode
